use std::marker::PhantomData;
use std::pin::Pin;

use bytes::Bytes;
use futures::Future;
use hashbrown::HashMap;
use honeybee::prelude::*;
use honeybee_proto::hall;
use once_cell::sync::Lazy;

use super::user::User;

type Process<A> = fn(&Addr<A>, Bytes) -> honeybee::Result<()>;

pub struct Register<A>(HashMap<u16, Process<A>>, PhantomData<A>);

impl<A> Register<A> {
    fn new() -> Self {
        Self(HashMap::default(), PhantomData)
    }

    pub fn register<T: 'static + prost::Message + MessageId + Default>(&mut self)
    where
        A: AnyHandler<T>,
    {
        let r = self.0.insert(T::ID, |user_addr, bytes| {
            let message = T::decode(bytes)?;
            user_addr.any_send(message)
        });
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub fn process(&self, user_addr: &Addr<A>, msg: UserRawMsg) -> honeybee::Result<()> {
        let process = self
            .0
            .get(&msg.id)
            .ok_or_else(|| anyhow::anyhow!("msssage id:{} not registry", msg.id))?;
        process(user_addr, msg.bytes)
    }
}

pub fn message_process(user_addr: &Addr<User>, msg: UserRawMsg) -> honeybee::Result<()> {
    REGISTER.process(user_addr, msg)
}

type AppUserProcess<A> = Box<
    dyn for<'a> Fn(
            Vec<u8>,
            &'a mut A,
            &'a mut Context<A>,
        ) -> Pin<Box<dyn Future<Output = honeybee::Result<()>> + Send + 'a>>
        + Send
        + Sync,
>;

pub struct AppUserRegister<A>(HashMap<u16, AppUserProcess<A>>, PhantomData<A>);

impl<A> AppUserRegister<A> {
    fn new() -> Self {
        Self(HashMap::default(), PhantomData)
    }

    pub fn register<T: Message<Result = ()> + HoneybeeMessage>(&mut self)
    where
        A: Handler<T>,
    {
        let r = self.0.insert(
            T::ID,
            Box::new(move |bytes, actor, ctx| {
                Box::pin(async move {
                    let message: T = bincode::deserialize(&bytes)?;
                    if let Err(err) = Handler::handle(actor, ctx, message).await {
                        error!("actor:{} handle msg err:{}", actor.name(), err);
                    }
                    Ok(())
                })
            }),
        );
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub async fn process(
        &self,
        msg_id: u16,
        bytes: Vec<u8>,
        actor: &mut A,
        ctx: &mut Context<A>,
    ) -> honeybee::Result<()> {
        let process = self
            .0
            .get(&msg_id)
            .ok_or_else(|| anyhow::anyhow!("app user msssage id:{} not registry", msg_id))?;
        process(bytes, actor, ctx).await
    }
}

pub async fn appuser_message_process(
    msg_id: u16,
    bytes: Vec<u8>,
    actor: &mut User,
    ctx: &mut Context<User>,
) -> honeybee::Result<()> {
    APPUSER_REGISTER.process(msg_id, bytes, actor, ctx).await
}

static REGISTER: Lazy<Register<User>> = Lazy::new(|| {
    let mut register = Register::new();
    register.register::<hall::HeartBeat>();
    register
});

static APPUSER_REGISTER: Lazy<AppUserRegister<User>> = Lazy::new(|| {
    let mut register = AppUserRegister::new();
    register.register::<proto::RoomChangeAppUserData>();
    register.register::<proto::UscRepeatOnlineKickout>();
    register.register::<proto::NewUmsgNotify>();
    register
});
